package com.tianx.factory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tianx.element.Item;
import com.tianx.element.ItemField;
import com.tianx.exception.FactoryException;
import com.tianx.screen.SystemFrame;
import com.tianx.util.FactoryUtil;

import javax.swing.*;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 管理系统的工厂
 * @author Bili_TianX
 */
public class Factory {
    /**
     * 目标类
     */
    public Class<?> targetClass;
    /**
     * 目标类的构造器
     */
    public Constructor<?> targetClassConstructor;
    /**
     * 类的名称
     */
    public String classValue;

    /**
     * 类的所有字段
     */
    public List<Field> fields;
    /**
     * 字段的getter
     */
    public Map<Field, Method> getMethods;
    /**
     * 字段的setter
     */
    public Map<Field, Method> setMethods;
    /**
     * 字段的名称
     */
    public Map<Field, String> fieldValue;
    /**
     * 系统中的所有对象
     */
    public ArrayList<Object> list;

    /**
     * 系统窗口
     */
    public SystemFrame frame;

    /**
     * 将目标类转化为工厂
     *
     * @param targetClass 目标类
     * @throws FactoryException 类处理异常
     */
    public Factory(Class<?> targetClass) throws FactoryException {
        if (!targetClass.isAnnotationPresent(Item.class)) {
            throw new FactoryException(
                    String.format(
                            "targetClass \"%s\" doesn't has Annotation \"%s\"!",
                            targetClass.getName(),
                            Item.class.getName()
                    )
            );
        }
        this.classValue = targetClass.getAnnotation(Item.class).value();
        this.fields = new ArrayList<>();
        this.getMethods = new HashMap<>();
        this.setMethods = new HashMap<>();
        this.list = new ArrayList<>();
        this.fieldValue = new HashMap<>();
        this.targetClass = targetClass;

        try {
            this.targetClassConstructor = this.targetClass.getConstructor();
        } catch (NoSuchMethodException e) {
            throw new FactoryException(
                    String.format(
                            "The class \"%s\" has no Parameterless Constructor!",
                            targetClass.getName()
                    )
            );
        }

        for (Field field : targetClass.getFields()) {
            if (field.isAnnotationPresent(ItemField.class)) {
                try {
                    FactoryUtil.transform(field.getType()).getConstructor(String.class);
                } catch (NoSuchMethodException e) {
                    throw new FactoryException("The type \"" + field.getType().getTypeName() + "\" has no constructor(String)!");
                }
                String getterName = "get" + FactoryUtil.capitalize(field.getName());
                String setterName = "set" + FactoryUtil.capitalize(field.getName());
                try {
                    getMethods.put(field, targetClass.getMethod(getterName));
                } catch (NoSuchMethodException e) {
                    throw new FactoryException(
                            String.format(
                                    "The Field \"%s\" of \"%s\" does not has getter \"%s\"!",
                                    field.getName(),
                                    targetClass.getName(),
                                    getterName
                            )
                    );
                }
                try {
                    setMethods.put(field, targetClass.getMethod(setterName, field.getType()));
                } catch (NoSuchMethodException e) {
                    throw new FactoryException(
                            String.format(
                                    "The Field \"%s\" of \"%s\" does not has setter \"%s\"!",
                                    field.getName(),
                                    targetClass.getName(),
                                    setterName
                            )
                    );
                }
                this.fields.add(field);
                this.fieldValue.put(field, field.getAnnotation(ItemField.class).value());
            }
        }
        load();
        frame = new SystemFrame(this);
    }

    /**
     * 将所有字段都转化为对应名称
     *
     * @return 所有字段的对应名称
     */
    public String[] fieldToArray() {
        String[] a = new String[fields.size()];
        for (int i = 0; i < fields.size(); i++) a[i] = fieldValue.get(fields.get(i));
        return a;
    }

    /**
     * 通过对话框添加对象
     *
     * @throws FactoryException 无法实例化类
     */
    public void add() throws FactoryException {
        try {
            Object o = targetClassConstructor.newInstance();
            for (Field field : fields) {
                Object value = FactoryUtil.getValueDialog(field, fieldValue.get(field), "添加" + classValue);
                if (value == null) return;
                setMethods.get(field).invoke(o, value);
            }
            list.add(o);
        } catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
            throw new FactoryException("Unable to instantiate \"" + targetClass.getName() + "\"");
        }
    }

    /**
     * 通过对话框删除对象
     */
    public void remove() {
        Object[] fieldName = fieldValue.values().toArray();
        String type = String.valueOf(JOptionPane.showInputDialog(
                null,
                "筛选方式",
                "删除" + classValue,
                JOptionPane.PLAIN_MESSAGE,
                null,
                fieldName,
                fieldName[0]
        ));
        if (type == null || type.equals("null")) return;

        Field field = FactoryUtil.getFieldByName(fieldValue, type);
        Method getter = getMethods.get(field);
        Object v = FactoryUtil.getValueDialog(field, fieldValue.get(field), "输入需要删除的" + classValue + "的" + type);
        list.removeIf(o -> {
            try {
                return getter.invoke(o).equals(v);
            } catch (IllegalAccessException | InvocationTargetException e) {
                return false;
            }
        });
    }


    /**
     * 从json文件中读取所有对象
     */
    @SuppressWarnings("ConstantConditions")
    public void load() {
        File file = new File(classValue + ".json");
        JSONArray array = null;
        if (!file.exists()) return;

        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
            StringBuilder builder = new StringBuilder();
            String temp;
            while ((temp = reader.readLine()) != null)
                builder.append(temp);
            array = JSON.parseArray(builder.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            for (Object o : array) {
                JSONObject jsonObject = (JSONObject) o;
                Object instance = targetClassConstructor.newInstance();
                for (Field field : fields) {
                    Object value = jsonObject.get(fieldValue.get(field));
                    setMethods.get(field).invoke(instance, FactoryUtil.transform(field.getType()).getConstructor(value.getClass()).newInstance(value));
                }
                list.add(instance);
            }
        } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    /**
     * 将所有对象保存至文件
     */
    public void save() throws InvocationTargetException, IllegalAccessException {
        File file = new File(classValue + ".json");
        JSONArray objects = new JSONArray();
        for (Object o : list) {
            JSONObject object = new JSONObject();
            for (Field field : o.getClass().getFields()) {
                Object invoke = getMethods.get(field).invoke(o);
                object.put(fieldValue.get(field), invoke.toString());
            }
            objects.add(object);
        }
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
            writer.write(objects.toJSONString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 查询所有符合条件的对象
     *
     * @param field 需要查询的字段
     * @param value 需要查询的值
     * @return 符合条件的对象
     */
    public List<Object> query(Field field, String value) {
        List<Object> resultList = new ArrayList<>();
        for (Object o : list) {
            try {
                if (getMethods.get(field).invoke(o).toString().equals(value))
                    resultList.add(o);
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        return resultList;
    }
}
